//
// Created by YE on 2024/1/3.
//
#include "lexer.h"

static TK_ get_Token_Type(String &s) {
    if (s == "true")return TK_TRUE;
    if (s == "false")return TK_FALSE;
    if (s == "else")return TK_ELSE;
    if (s == "if")return TK_IF;
    if (s == "while")return TK_WHILE;
    if (s == "Func")return TK_FUNCTION;
    if (s == "return")return TK_RETURN;
    if (s == "Int")return TK_INT;
    if (s == "Float")return TK_FLOAT;
    if (s == "Mat")return TK_MAT;
    if (s == "String")return TK_STRING;
    if (s == "Bool")return TK_BOOL;
    if (s == "break")return TK_BREAK;
    return TK_NAME;
}


static CType get_CType(char ch) {
    if ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_') return letter;
    if (ch == '\t' || ch == ' ') return blank;
    if (ch == '\n') return enter;
    if (ch == '0') return digit0;
    if ('1' <= ch && ch <= '9') return digit_;
    if (ch == '=') return eq;
    if (ch == '.') return dot;
    if (ch == '*') return ast;
    if (ch == '!') return ex;
    if (ch == '>') return greater;
    if (ch == '<') return less;
    if (ch == '+' || ch == '-' || ch == '/' || ch == '{' || ch == '}' || ch == ';' || ch == ',' || ch == '[' ||
        ch == ']' || ch == '(' || ch == ')' || ch == '%' || ch == '|' || ch == '&' )
        return single;
    if (ch == '\"') return qm;
    if (ch == '\0') return eof;
    return illegal;
}

static State change(State &state, CType cType) {
    State t;
    switch (state) {
        case 1:
            switch (cType) {
                case blank:
                case enter:
                    t = 1;
                    break;
                case letter:
                    t = 2;
                    break;
                case digit0:
                    t = 4;
                    break;
                case digit_:
                    t = 3;
                    break;
                case single:
                    t = 104;
                    break;
                case dot:
                    t = 7;
                    break;
                case eq:
                    t = 8;
                    break;
                case ast:
                    t = 13;
                    break;
                case ex:
                    t = 9;
                    break;
                case qm:
                    t = 10;
                    break;
                case greater:
                    t = 11;
                    break;
                case less:
                    t = 12;
                    break;
                case eof:
                    t = 100;
                    break;
                default:
                    t = 200;
            }
            break;
        case 2:
            switch (cType) {
                case letter:
                case digit0:
                case digit_:
                    t = 2;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 101;
            }
            break;
        case 3:
            switch (cType) {
                case letter:
                    t = 50;
                    break;
                case digit0:
                case digit_:
                    t = 3;
                    break;
                case dot:
                    t = 5;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 102;
            }
            break;
        case 4:
            switch (cType) {
                case letter:
                    t = 50;
                    break;
                case digit0:
                    t = 4;
                    break;
                case digit_:
                    t = 51;
                    break;
                case dot:
                    t = 5;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 102;
            }
            break;
        case 5:
            switch (cType) {
                case letter:
                    t = 50;
                    break;
                case digit0:
                case digit_:
                    t = 6;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 103;
            }
            break;
        case 6:
            switch (cType) {
                case letter:
                    t = 50;
                    break;
                case digit0:
                case digit_:
                    t = 6;
                    break;
                case dot:
                    t = 202;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 103;
            }
            break;
        case 7:
            switch (cType) {
                case digit0:
                case digit_:
                    t = 6;
                    break;
                case ast:
                    t = 105;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 108;
            }
            break;
        case 8:
            switch (cType) {
                case eq:
                    t = 106;
                    break;
                default:
                    t = 108;
            }
            break;
        case 9:
            switch (cType) {
                case eq:
                    t = 107;
                    break;
                default:
                    t = 108;
            }
            break;
        case 10:
            switch (cType) {
                case qm:
                    t = 109;
                    break;
                case enter:
                case eof:
                    t = 204;
                    break;
                default:
                    t = 10;
            }
            break;
        case 11:
            switch (cType) {
                case eq:
                    t = 110;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 108;
            }
            break;
        case 12:
            switch (cType) {
                case eq:
                    t = 111;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 108;
            }
            break;
        case 13:
            switch (cType) {
                case ast:
                    t = 112;
                    break;
                case qm:
                    t = 204;
                    break;
                default:
                    t = 108;
            }
            break;
        case 50:
            switch (cType) {
                case letter:
                case digit0:
                case digit_:
                    t = 50;
                    break;
                default:
                    t = 201;
            }
            break;
        case 51:
            switch (cType) {
                case letter:
                    t = 50;
                    break;
                case digit0:
                case digit_:
                    t = 51;
                    break;
                default:
                    t = 202;
            }
            break;
        default:
            t = 200;
    }
    state = t;
    return state;
}

Lexer::Lexer(Reader *reader) {
    this->reader = reader;
}

Lexer::~Lexer() = default;

void Lexer::skip_blanks() const {
    CType x = get_CType(reader->ch);
    while (x == blank || x == enter) {
        reader->next_char();
        x = get_CType(reader->ch);
    }
}

Lexer_State Lexer::get_next_Token(Token *token) {
    //100结束退出  101key或者id 102整数 103浮点 104单符号 105 .* 106 == 107 != 108延后判断
    //200字符错误 201标识符错误 202数字错误 203符号错误
    skip_blanks();
    State st = 1;
    size_t pos = reader->pos;
    size_t length;
    while (change(st, get_CType(reader->ch)) < 100) reader->next_char();
    length = reader->pos - pos;

    if (st == 101) {
        std::string substr = reader->str.substr(pos, length);
        token->type = get_Token_Type(substr);
        if (token->type == TK_NAME) {
            token->s = substr;
        }
        return LEXER_OK;
    }
    if (st == 102) {
        token->type = TK_INT_V;
        token->n.i = std::stoll(reader->str.substr(pos, length));
        return LEXER_OK;
    }
    if (st == 103) {
        token->type = TK_FLOAT_V;
        token->n.f = std::stod(reader->str.substr(pos, length));
        return LEXER_OK;
    }
    if (st == 104) {
        token->type = reader->ch;
        reader->next_char();
        return LEXER_OK;
    }
    if (st == 105) {
        token->type = TK_INNER;
        reader->next_char();
        return LEXER_OK;
    }
    if (st == 106) {
        token->type = TK_EQUAL;
        reader->next_char();
        return LEXER_OK;
    }
    if (st == 107) {
        token->type = TK_NOTEQUAL;
        reader->next_char();
        return LEXER_OK;
    }
    if (st == 108) {
        token->type = reader->get_pre_char();
        return LEXER_OK;
    }
    if (st == 109) {
        reader->next_char();
        token->type = TK_STRING_V;
        token->s = reader->str.substr(pos + 1, length - 1);
        return LEXER_OK;
    }
    if (st == 110) {
        reader->next_char();
        token->type = TK_GREATEREQUAL;
        return LEXER_OK;
    }
    if (st == 111) {
        reader->next_char();
        token->type = TK_LESSEQUAL;
        return LEXER_OK;
    }
    if (st == 112) {
        reader->next_char();
        token->type = TK_POW;
        return LEXER_OK;
    }
    if (200 <= st && st <= 204) {
        if (st == 200 || st == 204) reader->next_char();
        token->type = TK_ILLEGAL;
        throw_error(YCH_LEXER_ERROR, error_string(pos, st));
        return LEXER_ERROR;
    }
    if (st == 100) {
        token->type = TK_EOF;
        return LEXER_OK;
    }
    token->type = TK_ILLEGAL;
    return LEXER_ERROR;
}

String Lexer::error_string(size_t start_pos, State error_type) const {
    size_t file_pos = reader->line_start_pos, file_end = reader->pos;

    file_end = reader->str.find('\n', file_end);
    size_t length = file_end - file_pos;
    std::string s = reader->str.substr(file_pos, length);

    s.append(" line:" + std::to_string(reader->line));
    switch (error_type) {
        case 200:
            s.append(" Character error\n");
            break;
        case 201:
            s.append(" Identifier error\n");
            break;
        case 202:
            s.append(" Number error\n");
            break;
        case 203:
            s.append(" Operator error\n");
            break;
        case 204:
            s.append(" String error\n");
            break;
        default:
            s.append("\n");
    }
    size_t e = start_pos - file_pos;
    for (size_t x = 0; x < e; x++) {
        s.append(" ");
    }
    s.append("^");
    e = reader->pos - start_pos;
    for (size_t x = 1; x < e; x++) {
        s.append("~");
    }
    return s;
}

Token::Token() = default;

Token::~Token() = default;